# -*- coding: utf-8 -*-

import os

from ir.ir_module import IModule
from ir.ir_helper import Helper

from .convert_pack import convert_pack
from .convert_enum import convert_enum
from .convert_lane import convert_lane
from .convert_channel import convert_channel
from .convert_node import convert_node_struct
from .ast_utils import *
from .config import HLM_LIB_NAME, INCLUDE_PATH


BUILTIN_LIBS = ['enum']


def convert_imports(ast_imports: list, filepath: str, helper: Helper, builtin: dict):
    # Get the file path.
    file_dir = os.path.dirname(filepath)
    working_dir = os.path.abspath(os.curdir)
    # Create the include information.
    include_files = []
    # Loop for all the import files.
    for ast_import in ast_imports:
        if isinstance(ast_import, ast.ImportFrom):
            # Save the target module.
            target_name = ast_import.module
        else:
            raise Exception('Unimplemented import method {}'.format(type(ast_import)))
        # Ignore the module start from HLM.
        if target_name.startswith(HLM_LIB_NAME + '.'):
            continue
        # Ignore built-in types.
        if target_name in BUILTIN_LIBS:
            continue
        # Convert the absolute path into the target path.
        os.chdir(file_dir)
        # Construct the include path based on the file path.
        file_include_path = INCLUDE_PATH.copy()
        for ii, path in enumerate(file_include_path):
            file_include_path[ii] = os.path.abspath(path)
        # Back to my working directory.
        os.chdir(working_dir)
        # Start to search files.
        target_level = target_name.split('.')
        include_files.append(target_level.copy())
        target_level[-1] += '.py'
        target_path = None
        # Loop in all the include paths.
        for path in file_include_path:
            test_path = os.path.join(path, *target_level)
            if os.path.isfile(test_path):
                target_path = test_path
                break
        if target_path is None:
            raise Exception('Failed to find module {}'.format(target_name))
        # Convert the target module.
        target_module = convert_module(target_path, builtin)
        # Based on the helper of target module, update current helper.
        helper.types.update(target_module.helper.types)
        helper.chelper.update(target_module.helper.chelper)
    return include_files


def convert_module_defines(ast_list, convert_method, module_ir_list, helper: Helper):
    # Loop for all the defines in the list.
    for ast_def in ast_list:
        # Use convert method to convert.
        ir_list, helper_list = convert_method(ast_def, helper)
        # Add ir list to module ir list.
        module_ir_list += ir_list
        # Loop and add all the items to helper.
        for item_ir, item_helper in zip(ir_list, helper_list):
            helper.types[item_ir.name] = item_ir
            helper.chelper[item_ir.name] = item_helper


def convert_module(filepath: str, builtin: dict):
    # Parse the file into AST.
    ast_file = convert_to_ast(filepath)
    # Search for definitions.
    ast_file = split_file_ast(ast_file)
    # As for module, it does not need to parse the task, but only parse the
    # structure.
    # Construct the module IR.
    ir_module = IModule()
    ir_module.helper.chelper = builtin.copy()
    # Parse the import data.
    ir_module.includes = convert_imports(ast_file['import'], filepath, ir_module.helper, builtin)
    # Parse the enum, lane and channel data.
    convert_module_defines(ast_file['Enum'], convert_enum, ir_module.enums, ir_module.helper)
    convert_module_defines(ast_file['Pack'], convert_pack, ir_module.packs, ir_module.helper)
    convert_module_defines(ast_file['Lane'], convert_lane, ir_module.lanes, ir_module.helper)
    convert_module_defines(ast_file['Channel'], convert_channel, ir_module.channels, ir_module.helper)
    # Parse the node type.
    convert_module_defines(ast_file['Node'], convert_node_struct, ir_module.nodes, ir_module.helper)
    # Now give back all the modules.
    return ir_module
